Since 2015, JavaScript has improved immensely.
It’s much more pleasant to use it now than ever.
In this article, we’ll look at JavaScript iterable objects and generator functions.
Clean Up of Generators
If we use break
to break a loop, then we can clean up with the try-finally constructor.
For example, if we have:
function* genFn() {
yield 'foo';
yield 'bar';
yield 'baz';
console.log('clean up');
}
const gen = genFn();
for (const x of gen) {
console.log(x);
}
Then the console log will run when the generator returned all the items.
However, if we use break
in our loop:
for (const x of gen) {
console.log(x);
break;
}
Then the console log is never run.
We can run the cleanup step when we use break
in our loop by wrapping the yield
code with try-finally.
For example, we can write:
function* genFn() {
try {
yield 'foo';
yield 'bar';
yield 'baz';
} finally {
console.log('clean up');
}
}
const gen = genFn();
for (const x of gen) {
console.log(x);
break;
}
We added the finally
block, which will run when the break
statement is run.
This is useful for running clean up code on our generator.
If we implement our own iterator, we can put the function in our iterator’s return
method:
const obj = {
[Symbol.iterator]() {
function hasNextValue() {
//...
}
function getNextValue() {
//...
}
function cleanUp() {
//...
}
return {
next() {
if (hasNextValue()) {
const value = getNextValue();
return {
done: false,
value: value
};
} else {
//...
return {
done: true,
value: undefined
};
}
},
return () {
cleanUp();
}
};
}
}
We have the return
method included in the object we return with the Symbol.iterator
method.
Closing Iterators
We can close any iterators by creating our own generator function to close it.
For instance, we can write:
function* take(n, iterable) {
for (const x of iterable) {
if (n <= 0) {
break;
}
n--;
yield x;
}
}
We call break
on the loop to end it with when the ending condition is met.
Generators
Generators are pieces of code that we can pause and resume.
It’s denoted by the function*
keyword for generator functions.
yield
is an operation that a generator used to pause itself.
Generators can receive input and send output with yield
.
We can create a generator function by writing:
function* genFn() {
yield 'foo';
yield 'bar';
yield 'baz';
}
This returns a generator object which we can call next
on to return the values that are yield:
const genObj = genFn();
console.log(genObj.next());
console.log(genObj.next());
console.log(genObj.next());
We call genFn
to return a generator.
Then we call next
to get each value sequentially.
So we get:
{value: "foo", done: false}
{value: "bar", done: false}
{value: "baz", done: false}
We return an object with the value
and done
property.
The value
has the value from yield
.
done
tells us whether all the values have been yield from the generator.
Conclusion
We can clean up our generator by adding a try-finally block.
Also, generator functions create generators that let us return the value sequentially.